# coding:utf-8
"""
Created on 15/6/16

@author: shenyufeng
"""
import re
import time
import json
import functools
from eve.io.mongo import MongoJSONEncoder
from raven.contrib.tornado import SentryMixin

from tornado import web
from tornado.web import RequestHandler
from bson.objectid import ObjectId

import config
from common import myfilter
from common import session

"""document status dict"""
ON = 1
OFF = 2
DELETE = 3
rdb = config.rdb
mdb = config.mdb
errorDesc = config.errorDesc
Credentials = config.gconf.get("Access-Control-Allow-Credentials", [])
Allow_Origins = config.gconf.get("Access-Control-Allow-Origin", [])
Allow_Methods = config.gconf.get("Access-Control-Allow-Methods", [])
Expose_Headers = config.gconf.get("Access-Control-Expose-Headers", [])


def get_redisid(key):
    ids = rdb.hget('RedisID', key)
    if not ids:
        col_list = list(mdb[key].find().sort('_id', -1).limit(1))
        if len(col_list) == 0:
            ids = 10000000
        else:
            ids = col_list[0]['_id'] + (1 if col_list[0]['_id'] > 10000000 else 10000001)
        rdb.hset('RedisID', key, ids)
    return int(rdb.hincrby('RedisID', key, 1 if int(ids) > 10000000 else 10000001))


def getObjectID(key=None):
    return ObjectId()


def rtjson(code=1, **args):
    """return json"""
    if code == 1:
        args['status'] = 1
        args['response_time'] = int(time.time())
    else:
        args['status'] = 0
        args['error_code'] = code
        args['error_msg'] = errorDesc.get(code)
        args['response_time'] = int(time.time())

    res = json.dumps(args, cls=MongoJSONEncoder, sort_keys=True)
    return json.loads(res)


def rtjsonp(callback='callback', code=1, **args):
    """return jsonp"""
    if code == 1:
        args['status'] = 1
        args['response_time'] = int(time.time())
    else:
        args['status'] = 0
        args['error_code'] = code
        args['error_msg'] = errorDesc.get(code)
        args['response_time'] = int(time.time())
    return callback + '(' + json.dumps(args) + ');'


def mongoPager(docs, pagenum, pagesize=20):
    pager = cpage(docs.count(), pagenum, pagesize)
    doc_list = list(docs.skip(pager['start']).limit(pager['pagesize']))
    return doc_list, pager


def check_data(data, pattern):
    """
    数据正则校验
    """
    error_value = []
    for key, regex in pattern.items():
        value = data.get(key)
        if value is None:
            error_value.append((key, 'null'))
        elif regex:
            match = re.match(regex, value.decode('utf8'))
            if match is None:
                error_value.append((key, 'error'))

    return error_value


def cpage(total, pagenum=1, pagesize=20):
    pagenum = int(pagenum)
    pagecount = (total // pagesize) + (1 if total % pagesize != 0 else 0)
    pager = {
        'total': total,  # 记录总数
        'pagenum': pagenum,  # 请求页码
        'pagesize': pagesize,  # 每页条数
        'pagecount': int(pagecount),  # 页数
        'start': (pagenum - 1) * pagesize,
        'end': pagenum * pagesize
    }
    return pager


def mongo_pager(docs, pagenum, pagesize=20):
    pager = cpage(docs.count(), pagenum, pagesize)
    doc_list = list(docs.skip(pager['start']).limit(pager['pagesize']))
    return doc_list, pager


def pageList(docs, pagenum, pagesize=20):
    pager = cpage(len(docs), pagenum, pagesize)
    doc_list = docs[pager['start']:pager['end']]
    return doc_list, Storage(pager)


def mongo_limit(col_name, pre_id=0, query=None, id_sort=-1, pagesize=20):
    """
    mongo limit分页
    当数据量大时，skip的性能低
    :param col_name:mongo集合名称
    :param pre_id:上一条数据ID
    :param query:查询条件
    :param id_sort:排序
    :param pagesize:每页条数
    :return:
    """
    query = query if query else {}
    if pre_id:
        query['_id'] = {'$gt': pre_id} if id_sort else {'$lt': pre_id}

    return list(mdb[col_name].find(query).sort('_id', id_sort).limit(pagesize))


class BaseHandler(SentryMixin, RequestHandler):
    def set_default_headers(self):
        origin = self.request.headers.get("origin")
        method = self.request.headers.get("Access-Control-Request-Method")
        headers = self.request.headers.get("Access-Control-Request-Headers")
        if origin and (origin.startswith("http://192.168.") or origin in Allow_Origins):
            self.set_header('Access-Control-Allow-Origin', origin)

            if Credentials:
                self.set_header('Access-Control-Allow-Credentials', "true")
            if Expose_Headers:
                self.set_header('Access-Control-Expose-Headers', Expose_Headers)
            if method in Allow_Methods:
                self.set_header('Access-Control-Allow-Methods', method)
            if headers:
                self.set_header('Access-Control-Allow-Headers', headers)

    def options(self, *args, **kwargs):
        pass

    def get_current_user(self):
        if self.request.headers.get('Token'):  # access_token
            return self.get_secure_cookie("myid", self.request.headers.get('Token')).decode('utf-8')
        if self.get_secure_cookie('myid'):
            return self.get_secure_cookie('myid').decode('utf-8')

        return None

    def input(self, name, default=None, strip=True):
        if self.request.method != "GET":
            if self.request.body and self.request.body != '{}' and self.request.body.startswith(b'{'):
                obj = json.loads(self.request.body)
                return obj.get(name, default)
        return self._get_argument(name, default, self.request.arguments, strip)

    def get_template_namespace(self):
        namespace = super(BaseHandler, self).get_template_namespace()
        namespace.update(myfilter.filters)
        namespace['gconf'] = Storage(config.gconf)
        namespace['session'] = {'uname': self.get_current_user()}

        return namespace


class AppBaseHandler(SentryMixin, RequestHandler):
    # def __init__(self, *args, **kwargs):
    #     super(AppBaseHandler, self).__init__(*args, **kwargs)
    #     self.__session_manager = session.SessionManager(rdb)

    def set_default_headers(self):
        origin = self.request.headers.get("origin")
        method = self.request.headers.get("Access-Control-Request-Method")
        headers = self.request.headers.get("Access-Control-Request-Headers")
        if origin and (origin.startswith("http://192.168.") or origin in Allow_Origins):
            self.set_header('Access-Control-Allow-Origin', origin)
            if Credentials:
                self.set_header('Access-Control-Allow-Credentials', "true")
            if Expose_Headers:
                self.set_header('Access-Control-Expose-Headers', Expose_Headers)
            if method in Allow_Methods:
                self.set_header('Access-Control-Allow-Methods', method)
            if headers:
                self.set_header('Access-Control-Allow-Headers', headers)

    def options(self, *args, **kwargs):
        pass

    def get_current_user(self):
        if self.request.headers.get('Token'):  # access_token
            token = self.get_secure_cookie("sid", self.request.headers.get('Token'))
            if token:
                return token.decode('utf-8')
            return None
        if self.get_secure_cookie('sid'):
            return self.get_secure_cookie('sid').decode('utf-8')

        return None

    # session #
    # def get_session(self, key, myid=None):
    #     sessionid = self.get_current_user() if not myid else str(myid)
    #     if sessionid:
    #         return self.__session_manager.get_session(sessionid, key)
    #
    # def set_session(self, key, value, myid=None):
    #     sessionid = self.get_current_user() if not myid else str(myid)
    #     return self.__session_manager.set_session(sessionid, key, value)
    #
    # def set_mapping_session(self, mapping, myid=None):
    #     sessionid = self.get_current_user() if not myid else str(myid)
    #     return self.__session_manager.set_mapping_session(sessionid, mapping)
    #
    # def clear_session(self, key, myid=None):
    #     sessionid = self.get_current_user() if not myid else str(myid)
    #     return self.__session_manager.clear(sessionid, key)
    #
    # def clear_all_session(self, myid=None):
    #     sessionid = self.get_current_user() if not myid else str(myid)
    #     return self.__session_manager.clear_all(sessionid)

    # session #
    def input(self, name, default=None, strip=True):
        if self.request.method != "GET":
            if self.request.body and self.request.body != '{}' and self.request.body.startswith(b'{'):
                obj = json.loads(self.request.body)
                return obj.get(name, default)
        return self._get_argument(name, default, self.request.arguments, strip)

    def get_template_namespace(self):
        namespace = super(AppBaseHandler, self).get_template_namespace()
        namespace.update(myfilter.filters)
        namespace['gconf'] = Storage(config.gconf)
        namespace['session'] = {'uname': self.get_current_user()}

        return namespace


class WxHandler(BaseHandler):

    def __init__(self, *args, **kwargs):
        super(WxHandler, self).__init__(*args, **kwargs)
        self.__session_manager = session.SessionManager(rdb)

    def get_unionid(self):
        if self.get_secure_cookie('unionid'):
            return self.get_secure_cookie('unionid').decode('utf-8')

    def get_current_user(self):
        if self.get_secure_cookie('unionid') is None:
            return None
        myid = None
        if self.request.headers.get('Token'):  # access_token
            myid = self.get_secure_cookie("myid", self.request.headers.get('Token')).decode('utf-8')
        if self.get_secure_cookie('myid'):
            myid = self.get_secure_cookie('myid').decode('utf-8')
        if myid:
            unionid = self.get_secure_cookie('unionid').decode('utf-8')
            if self.get_session("myid", myid) and str(self.get_session("unionid", myid)) == str(unionid):
                return myid
            else:
                wxinfo = mdb.wxinfo.find_one({"unionid": unionid})
                wxuser = mdb.wxuser.find_one({"unionid": wxinfo.get("unionid")})
                if wxuser:
                    myid = wxuser["_id"]
                    self.set_mapping_session({"myid": wxuser["_id"], "wid": wxinfo["_id"],
                                              'unionid': wxinfo.get('unionid')}, str(myid))
                    self.set_secure_cookie("myid", str(myid))
                    self.set_secure_cookie("xuid", str(myid))
                    return myid
                else:
                    self.clear_cookie("myid")
                    self.clear_cookie("xuid")
                    myid = None
        return myid

    # session #
    def get_session(self, key, myid=None):
        sessionid = self.get_current_user() if not myid else str(myid)
        if sessionid:
            return self.__session_manager.get_session(sessionid, key)

    def set_session(self, key, value, myid=None):
        sessionid = self.get_current_user() if not myid else str(myid)
        return self.__session_manager.set_session(sessionid, key, value)

    def set_mapping_session(self, mapping, myid=None):
        sessionid = self.get_current_user() if not myid else str(myid)
        return self.__session_manager.set_mapping_session(sessionid, mapping)

    def clear_session(self, key, myid=None):
        sessionid = self.get_current_user() if not myid else str(myid)
        return self.__session_manager.clear(sessionid, key)

    def clear_all_session(self, myid=None):
        sessionid = self.get_current_user() if not myid else str(myid)
        return self.__session_manager.clear_all(sessionid)
    # session #


class AdminHandler(BaseHandler):
    def get_current_user(self):
        if self.get_secure_cookie('admid'):
            return self.get_secure_cookie('admid').decode('utf-8')
        return None

    def get_login_url(self):
        return '/m/login'

    def get_template_namespace(self):
        namespace = super(AdminHandler, self).get_template_namespace()
        namespace.update(myfilter.filters)
        namespace['gconf'] = Storage(config.gconf)
        return namespace


class MiniAppHandler(BaseHandler):

    def __init__(self, *args, **kwargs):
        super(MiniAppHandler, self).__init__(*args, **kwargs)
        self.__session_manager = session.SessionManager(rdb)

    def prepare(self):
        # ori = self.request.headers.get("origin")
        # if ori and (ori.startswith("http://192.168.") or ori in Origin):
        #     self.set_header('Access-Control-Allow-Origin', ori)
        #     if Credentials:
        #         self.set_header('Access-Control-Allow-Credentials', "true")
        #     if ExposeHeaders:
        #         self.set_header('Access-Control-Expose-Headers', ExposeHeaders)
        pass

    def get_current_user(self):
        # if config.run_mode == "local":
            # ObjectId("5aa09a2afb49d2029aed4e9d")
            # return "5adfe549fb49d209bddf5656"
        if self.request.headers.get('Token'):  # access_token
            return self.get_secure_cookie("cid", self.request.headers.get('Token')).decode('utf-8')
        return self.get_secure_cookie("cid")

    # session #
    def get_session(self, key, key_id=None):
        sessionid = self.get_current_user() if not key_id else str(key_id)
        if sessionid:
            return self.__session_manager.get_session(sessionid, key)

    def set_session(self, key, value, key_id=None, expires=7200):
        sessionid = self.get_current_user() if not key_id else str(key_id)
        return self.__session_manager.set_session(sessionid, key, value,
                                                  expires=expires)

    def set_mapping_session(self, mapping, key_id=None, expires=7200):
        sessionid = self.get_current_user() if not key_id else str(key_id)
        return self.__session_manager.set_mapping_session(sessionid, mapping,
                                                          expires=expires)

    def clear_session(self, key, key_id=None):
        sessionid = self.get_current_user() if not key_id else str(key_id)
        return self.__session_manager.clear(sessionid, key)

    def clear_all_session(self, key_id=None):
        sessionid = self.get_current_user() if not key_id else str(key_id)
        return self.__session_manager.clear_all(sessionid)

    # session #

    def input(self, name, default=None, strip=True):
        if self.request.method != "GET":
            if self.request.body and self.request.body != '{}' and self.request.body.startswith(b'{'):
                obj = json.loads(self.request.body)
                return obj.get(name, default)
        return self._get_argument(name, default, self.request.arguments, strip)

    # def get_template_namespace(self):
    #     namespace = super(BaseHandler, self).get_template_namespace()
    #     namespace.update(myfilter.filters)
    #     namespace['gconf'] = Storage(config.gconf)
    #     namespace['session'] = {'uname': self.get_current_user()}
    #     return namespace


class Storage(dict):
    """
    A Storage object is like a dictionary except `obj.foo` can be used
    in addition to `obj['foo']`.
    """

    def __getattr__(self, key):
        try:
            return self.get(key)
        except KeyError as k:
            raise AttributeError(k)

    def __setattr__(self, key, value):
        self[key] = value

    def __delattr__(self, key):
        try:
            del self[key]
        except KeyError as k:
            raise AttributeError(k)

    def __repr__(self):
        return '<Storage ' + dict.__repr__(self) + '>'


def authenticated(method):
    @functools.wraps(method)
    def wrapper(self, *args, **kwargs):
        if not self.current_user:
            # from tornado import web
            raise web.HTTPError(403)
        return method(self, *args, **kwargs)

    return wrapper
